package jrobutt.room.level;

import jrobutt.*;
import jrobutt.room.ExplosionParticleEmitter;

import java.awt.*;
import java.awt.geom.*;

public class Guy extends GameObject
{
    public static final float DEFAULT_MOVE_SPD = 2.0f;
    
    // these are pretty much constant throughout the entirety of the game
    // i like to call these sorts of variables the "data sheet"
    public float move_spd;
    public float slide_bounce_factor; // this.speed() gets multiplied by this when hitting a wall
    public float slide_dec; // slide deceleration
    
    public float radius;
    public float oil_hole_radius;
    
    // relevant member(s) for state "normal" or "moving" (not sure if i'm
    // gonna have "moving" be its own state since i don't see a need for two
    // different states to differentiate between "moving" and "stationary")
    public Vector2 move_vecdir;
    
    // this is a "calculated" or "resultant" value that shouldn't ever
    // get modified manually/directly
    public Vector2 facing_direction; 
    
    public Vector2 oil_hole; // also calculated/resultant
    
    // relevant members for state "hit" or "stunned" or whatever
    // im gonna eventually call it
    public boolean is_hit;
    // the next two values are meaningless unless (is_hit == true)
    public Vector2 slide_direction;
    public float slide_speed;
    public float slide_timer;
    
    public boolean is_dead;
    
    public boolean can_move; // this will generally get controller via external objects
    
    public GameSprite normal_sprite;
    public GameSprite dead_sprite;
    public ExplosionParticleEmitter exploder;
    
    public Guy (float x, float y, Vector2 facing_direction,
                GameSprite normal_sprite, GameSprite dead_sprite,
                ExplosionParticleEmitter exploder)
    {
        super(x, y, normal_sprite);
        
        this.normal_sprite = normal_sprite;
        this.dead_sprite = dead_sprite;
        
        this.exploder = exploder;
        
        radius = 8.0f;
        
        move_vecdir = new Vector2();
        
        this.facing_direction = facing_direction;
        
        oil_hole = new Vector2();
        oil_hole_radius = 4.0f;
        calculate_oil_hole();
        
        is_hit = false;
        slide_direction = new Vector2();
        slide_timer = -1.0f;
        slide_speed = 0.0f;
        
        is_dead = false;
        
        can_move = true;
        
        
        move_spd = DEFAULT_MOVE_SPD;
        slide_bounce_factor = 5.0f;
        float slide_duration = 7.0f;
        slide_dec = (move_spd * slide_bounce_factor) / slide_duration;
    }
    
    private void calculate_oil_hole ()
    {
        float r = radius;
        oil_hole.x = x - r * facing_direction.x;
        oil_hole.y = y - r * facing_direction.y;
    }
    
    public void kill ()
    {
        if (is_dead) return;
        
        is_dead = true;
        sprite = dead_sprite;
        exploder.explodeAt(x, y);
    }
    
    public void stop_moving ()
    {
        move_vecdir.set(Vector2.ZERO);
    }
    
    /* assume direction.getLength() == 1.0f */
    public void move_in_direction (Vector2 direction)
    {
        move_vecdir.set(direction);
    }
    
    public void update ()
    {
        if (slide_timer > 0.0f) // if slide_timer is active
        {
            slide_timer -= 1.0f;
            if (slide_timer <= 0.0f)
            {
                is_hit = false;
            }
        }
        
        if (is_dead)
        {
            xspeed = 0.0f;
            yspeed = 0.0f;
        }
        else if (is_hit) // state = "hit" or "stunned"
        {
            slide_speed -= slide_dec;
            if (slide_speed < 0.0f)
            {
                slide_speed = 0.0f;
            }
            xspeed = slide_speed * slide_direction.x;
            yspeed = slide_speed * slide_direction.y;
        }
        else if (can_move)
        {
            if (!move_vecdir.equals(Vector2.ZERO))
            {
                float move_vecdir_angle = move_vecdir.getAngle();
                float facing_angle = facing_direction.getAngle();
                // the next two lines are equivalent to delta_direction = move_vecdir - facing_direction
                Vector2 delta_direction = new Vector2(move_vecdir);
                // uses -facing_direction.y because that's what makes it the negative of move_vecdir
                delta_direction.rotate(facing_direction.x, -facing_direction.y);
                float delta_angle = delta_direction.getAngle();
                float target_angle;
                float turn = GameMath.Pi / 8.0f;
                if (Math.abs(delta_angle) <= turn)
                {
                    target_angle = move_vecdir_angle;
                }
                else
                {
                    target_angle = facing_angle + delta_angle / 2.0f;
                }
                facing_direction.setAngle(target_angle);
                xspeed = facing_direction.x * move_spd;
                yspeed = facing_direction.y * move_spd;
            }
            else
            {
                xspeed = 0.0f;
                yspeed = 0.0f;
            }
        }
        
        super.update();
        
        calculate_oil_hole();
    }
    
    public void hit_wall (Vector2 delta)
    {
    }
    
    public void render (Graphics2D g)
    {
        AffineTransform prev_transform = g.getTransform();
        {
            g.rotate(facing_direction.getAngle(), x, y);
            super.render(g);
        }
        g.setTransform(prev_transform);
    }
}
